如何将椭圆添加到 coreplot 条形图
how can I add an ellipse to a coreplot bargraph
条形图上带有椭圆的图像是所需的结果。我继承了创建显示人血压的条形图的代码。我的任务是在条形图的顶部绘制一个椭圆来显示人的心率。所以我使用相同的 x 轴,但我相信我需要添加另一个 Y 轴。这是代码。我添加了第二个 plotspace (plotspace2) 和另一个 y 轴 (rightY)。任何帮助将不胜感激
@interface BPGraphView()
@property (nonatomic) CPTXYGraph* graph;
@property (nonatomic) CPTBarPlot* plot;
@property (nonatomic) CPTScatterPlot* plotGoal;
@property (nonatomic) CPTBarPlot* plotHeartRate;
@end
@implementation BPGraphView
- (void)layoutSubviews {
[super layoutSubviews];
[self setupGraph];
[self setupAxes];
[self setupGoalAnnotation];
[self.graph reloadData];
}
- (void)setupGraph {
if (!self.graph) {
self.graph = [[CPTXYGraph alloc] init];
self.hostedGraph = self.graph;
self.graph.paddingTop = 0;
self.graph.paddingBottom = 0;
self.graph.paddingLeft = 0;
self.graph.paddingRight = 0;
// We need to rasterize the graph, otherwise performance is poor when scrolling
self.layer.shouldRasterize = YES;
UIParam* uiParam = [UIParam sharedUIParam];
// a single dashed line showing the daily goal
self.plotGoal = [[CPTScatterPlot alloc] init];
[self.graph addPlot:self.plotGoal];
self.plotGoal.dataSource = self;
CPTMutableLineStyle *lineStyleDash = [[CPTMutableLineStyle alloc] init];
lineStyleDash.lineWidth = 2;
lineStyleDash.lineColor = [CPTColor colorWithCGColor:uiParam.colorGoalLine.CGColor];
// lineStyleDash.dashPattern = @[ @7, @3 ];
lineStyleDash.lineJoin = kCGLineJoinRound;
self.plotGoal.dataLineStyle = lineStyleDash;
self.plot = [[CPTBarPlot alloc] init];
[self.graph addPlot:self.plot];
self.plot.dataSource = self;
self.plot.fill = [[CPTFill alloc] initWithColor:[CPTColor colorWithCGColor:[[UIColor colorWithHexString:kBarColorString] CGColor]]];
self.plot.lineStyle = nil;
self.graph.defaultPlotSpace.allowsUserInteraction = NO;
}
self.graph.frame = self.bounds;
self.plot.frame = CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height - 10.0);
}
- (void)setupAxes {
// Plot space ranges
//int dailyGoal = 0;//[self.data.stepsSummary.dailyStepsGoal.steps intValue];
float maxY = 190.0;
for (YMBPSeries* bpEntry in self.data.bpSeries) {
if (bpEntry.systolic > maxY)
{
maxY = bpEntry.systolic;
}
}
NSLog(@"MaxY = %f", maxY); // TODO: temp debug
CPTXYPlotSpace* plotSpace = (CPTXYPlotSpace*)self.graph.defaultPlotSpace;
if (self.data.dayInterval == YMDayInterval7Day) {
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-2) length:CPTDecimalFromFloat(9)]; // 7 values + a little extra before for y axis labels
}
else {
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-4) length:CPTDecimalFromFloat(18)]; // 14 values + a little extra before for y axis labels
}
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-maxY * .10) length:CPTDecimalFromFloat(maxY * 1.4)]; // add some space before (for labels) and after
self.plot.barWidth = CPTDecimalFromFloat(0.25);
//Heartrate Plot
CPTXYPlotSpace* plotSpace2 = [[CPTXYPlotSpace alloc] init];
plotSpace2.xRange = plotSpace.xRange;
plotSpace2.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0) length:CPTDecimalFromFloat(400.0)];
[_graph addPlotSpace:plotSpace2];
// Axes
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)self.graph.axisSet;
CPTXYAxis *xAxis = axisSet.xAxis;
xAxis.majorIntervalLength = CPTDecimalFromInt(1);
xAxis.orthogonalCoordinateDecimal = CPTDecimalFromInt(0);
xAxis.majorGridLineStyle = nil;
xAxis.minorGridLineStyle = nil;
xAxis.majorTickLineStyle = nil;
xAxis.minorTickLineStyle = nil;
xAxis.axisLineStyle = nil;
xAxis.labelingPolicy = CPTAxisLabelingPolicyNone;
CPTMutableTextStyle* textStyle = [CPTMutableTextStyle textStyle];
textStyle.fontName = kLabelFontName;
textStyle.fontSize = FONT_SIZE_AXIS_LABEL;
textStyle.color = [CPTColor blackColor];
xAxis.labelTextStyle = textStyle;
xAxis.axisLabels = [GraphUtil xAxisLabels:self.data.dayInterval start:self.data.startDate style:xAxis.labelTextStyle leftText:nil];
CPTXYAxis *yAxis = axisSet.yAxis;
yAxis.majorIntervalLength = CPTDecimalFromInt(10);
yAxis.minorTicksPerInterval = 0;
yAxis.orthogonalCoordinateDecimal = CPTDecimalFromInteger(0);
//Setup Heart Rate YAxis
CPTXYAxis *rightY = [(CPTXYAxis *)[CPTXYAxis alloc] initWithFrame:CGRectZero];
rightY.coordinate = CPTCoordinateY;
rightY.plotSpace = plotSpace2;
rightY.orthogonalCoordinateDecimal = CPTDecimalFromInteger(0);
CPTMutableLineStyle *majorGridLineStyle = [CPTMutableLineStyle lineStyle];
majorGridLineStyle.lineWidth = 0.20;
majorGridLineStyle.lineColor = [CPTColor lightGrayColor];
yAxis.majorGridLineStyle = majorGridLineStyle;
yAxis.minorGridLineStyle = nil;
yAxis.majorTickLineStyle = nil;
yAxis.minorTickLineStyle = nil;
yAxis.axisLineStyle = nil;
yAxis.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
yAxis.preferredNumberOfMajorTicks = 8;
NSNumberFormatter *labelFormatter = [[NSNumberFormatter alloc] init];
labelFormatter.numberStyle = NSNumberFormatterDecimalStyle;
yAxis.labelFormatter = labelFormatter;
yAxis.labelOffset = 0;
// yAxis.titleOffset = 54;
yAxis.title = @"mmHg";
rightY.majorGridLineStyle = majorGridLineStyle;
rightY.minorGridLineStyle = nil;
rightY.majorTickLineStyle = nil;
rightY.minorTickLineStyle = nil;
rightY.axisLineStyle = nil;
rightY.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
rightY.preferredNumberOfMajorTicks = 8;
NSNumberFormatter *labelFormatter2 = [[NSNumberFormatter alloc] init];
labelFormatter.numberStyle = NSNumberFormatterDecimalStyle;
rightY.labelFormatter = labelFormatter2;
rightY.labelOffset = 0;
rightY.title = @"bpm";
NSMutableArray* exclusionRanges = [NSMutableArray array];
// // do not label 0
// [exclusionRanges addObject:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(1) length:CPTDecimalFromFloat(-99999)]];
//
// // do not label the daily goal, or nearby, because it gets a custom annotation
// if (dailyGoal > 0) {
// [exclusionRanges addObject:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(dailyGoal - maxY*0.05) length:CPTDecimalFromFloat(maxY*.1)]];
// }
// do not label max number, because sometimes the label is cut off at the top, which looks bad
[exclusionRanges addObject:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(maxY+1) length:CPTDecimalFromFloat(99999)]];
yAxis.labelExclusionRanges = exclusionRanges;
// yAxis.title = @"mmHg";
}
// add an annotation to label the daily goal.
// It is positioned at the left of the graph, just to the right of the axis, above 12AM.
// At this position we are mostly safe from interference from the daily steps graph.
// Also we do not put it over the Y labels, since those are automatically generated.
- (void)setupGoalAnnotation {
[self.graph.plotAreaFrame.plotArea removeAllAnnotations];
NSNumber* dailyGoal = 0;
if ([dailyGoal intValue] > 0) {
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)self.graph.axisSet;
CPTXYAxis *yAxis = axisSet.yAxis;
// custom labels for threshholds
CPTMutableTextStyle* textStyleThreshhold = [yAxis.labelTextStyle mutableCopy];
textStyleThreshhold.fontName = kLabelFontName;
textStyleThreshhold.textAlignment = CPTTextAlignmentCenter;
textStyleThreshhold.fontSize = textStyleThreshhold.fontSize - 4.0;
UIParam* uiParam = [UIParam sharedUIParam];
CPTBorderedLayer* layerAnnotation = [GraphUtil borderedTextLayer: [dailyGoal description] style:textStyleThreshhold margin:CGSizeMake(4, 4)];
layerAnnotation.fill = [CPTFill fillWithColor:[CPTColor whiteColor]];
layerAnnotation.borderColor = uiParam.colorGoalLine.CGColor;
layerAnnotation.borderWidth = 2.0;
CGFloat x = 0.0;
switch (self.data.dayInterval) {
case YMDayInterval1Day:
x = 22.9;
break;
case YMDayInterval7Day:
x = 6.60;
break;
case YMDayInterval14Day:
x = 13.2;
break;
default:
break;
}
NSArray* anchorPoint = @[@(x), dailyGoal ];
CPTPlotSpaceAnnotation* annotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:self.graph.defaultPlotSpace anchorPlotPoint:anchorPoint];
annotation.contentLayer = layerAnnotation;
[self.graph.plotAreaFrame.plotArea addAnnotation:annotation];
}
}
#pragma mark - CPTPlotDataSource delegate
- (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
if (plot == self.plotGoal) {
return 2;
}
else {
if (self.data.dayInterval == YMDayInterval7Day)
return 7;
return 14;
}
}
- (NSNumber *)numberForPlot:(CPTPlot *)plot field: (NSUInteger)fieldEnum recordIndex:(NSUInteger)idx {
if (plot == self.plotGoal) {
if (fieldEnum == CPTScatterPlotFieldX) {
if (idx == 0)
return @(-1);
else
return @(9999);
}
else if (fieldEnum == CPTScatterPlotFieldY) {
return 0;//self.data.stepsSummary.dailyStepsGoal.steps;
}
}
else {
if (fieldEnum == CPTBarPlotFieldBarLocation) {
return @(idx);
}
else if (fieldEnum == CPTBarPlotFieldBarTip) {
NSDate* date = [self.data.startDate dateByAddingTimeInterval: (SECONDS_PER_DAY * idx)];
for (YMBPSeries* bpEntry in self.data.bpSeries) {
if ([bpEntry.date isUTCSameDay:date]) {
if (bpEntry.systolic > 0)
return @(bpEntry.systolic);
else
return nil;
}
}
}
}
return nil;
}
@end
您走在正确的轨道上。使用一个图 space 作为带血压的条形图,使用第二个图 space 作为心率的散点图。将散点图的 plotSymbol
设置为所需的椭圆形状并填充并设置 dataLineStyle
为 nil
。这将在每个数据点绘制一个绘图符号,它们之间没有连接线。
条形图上带有椭圆的图像是所需的结果。我继承了创建显示人血压的条形图的代码。我的任务是在条形图的顶部绘制一个椭圆来显示人的心率。所以我使用相同的 x 轴,但我相信我需要添加另一个 Y 轴。这是代码。我添加了第二个 plotspace (plotspace2) 和另一个 y 轴 (rightY)。任何帮助将不胜感激
@interface BPGraphView()
@property (nonatomic) CPTXYGraph* graph;
@property (nonatomic) CPTBarPlot* plot;
@property (nonatomic) CPTScatterPlot* plotGoal;
@property (nonatomic) CPTBarPlot* plotHeartRate;
@end
@implementation BPGraphView
- (void)layoutSubviews {
[super layoutSubviews];
[self setupGraph];
[self setupAxes];
[self setupGoalAnnotation];
[self.graph reloadData];
}
- (void)setupGraph {
if (!self.graph) {
self.graph = [[CPTXYGraph alloc] init];
self.hostedGraph = self.graph;
self.graph.paddingTop = 0;
self.graph.paddingBottom = 0;
self.graph.paddingLeft = 0;
self.graph.paddingRight = 0;
// We need to rasterize the graph, otherwise performance is poor when scrolling
self.layer.shouldRasterize = YES;
UIParam* uiParam = [UIParam sharedUIParam];
// a single dashed line showing the daily goal
self.plotGoal = [[CPTScatterPlot alloc] init];
[self.graph addPlot:self.plotGoal];
self.plotGoal.dataSource = self;
CPTMutableLineStyle *lineStyleDash = [[CPTMutableLineStyle alloc] init];
lineStyleDash.lineWidth = 2;
lineStyleDash.lineColor = [CPTColor colorWithCGColor:uiParam.colorGoalLine.CGColor];
// lineStyleDash.dashPattern = @[ @7, @3 ];
lineStyleDash.lineJoin = kCGLineJoinRound;
self.plotGoal.dataLineStyle = lineStyleDash;
self.plot = [[CPTBarPlot alloc] init];
[self.graph addPlot:self.plot];
self.plot.dataSource = self;
self.plot.fill = [[CPTFill alloc] initWithColor:[CPTColor colorWithCGColor:[[UIColor colorWithHexString:kBarColorString] CGColor]]];
self.plot.lineStyle = nil;
self.graph.defaultPlotSpace.allowsUserInteraction = NO;
}
self.graph.frame = self.bounds;
self.plot.frame = CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height - 10.0);
}
- (void)setupAxes {
// Plot space ranges
//int dailyGoal = 0;//[self.data.stepsSummary.dailyStepsGoal.steps intValue];
float maxY = 190.0;
for (YMBPSeries* bpEntry in self.data.bpSeries) {
if (bpEntry.systolic > maxY)
{
maxY = bpEntry.systolic;
}
}
NSLog(@"MaxY = %f", maxY); // TODO: temp debug
CPTXYPlotSpace* plotSpace = (CPTXYPlotSpace*)self.graph.defaultPlotSpace;
if (self.data.dayInterval == YMDayInterval7Day) {
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-2) length:CPTDecimalFromFloat(9)]; // 7 values + a little extra before for y axis labels
}
else {
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-4) length:CPTDecimalFromFloat(18)]; // 14 values + a little extra before for y axis labels
}
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-maxY * .10) length:CPTDecimalFromFloat(maxY * 1.4)]; // add some space before (for labels) and after
self.plot.barWidth = CPTDecimalFromFloat(0.25);
//Heartrate Plot
CPTXYPlotSpace* plotSpace2 = [[CPTXYPlotSpace alloc] init];
plotSpace2.xRange = plotSpace.xRange;
plotSpace2.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0) length:CPTDecimalFromFloat(400.0)];
[_graph addPlotSpace:plotSpace2];
// Axes
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)self.graph.axisSet;
CPTXYAxis *xAxis = axisSet.xAxis;
xAxis.majorIntervalLength = CPTDecimalFromInt(1);
xAxis.orthogonalCoordinateDecimal = CPTDecimalFromInt(0);
xAxis.majorGridLineStyle = nil;
xAxis.minorGridLineStyle = nil;
xAxis.majorTickLineStyle = nil;
xAxis.minorTickLineStyle = nil;
xAxis.axisLineStyle = nil;
xAxis.labelingPolicy = CPTAxisLabelingPolicyNone;
CPTMutableTextStyle* textStyle = [CPTMutableTextStyle textStyle];
textStyle.fontName = kLabelFontName;
textStyle.fontSize = FONT_SIZE_AXIS_LABEL;
textStyle.color = [CPTColor blackColor];
xAxis.labelTextStyle = textStyle;
xAxis.axisLabels = [GraphUtil xAxisLabels:self.data.dayInterval start:self.data.startDate style:xAxis.labelTextStyle leftText:nil];
CPTXYAxis *yAxis = axisSet.yAxis;
yAxis.majorIntervalLength = CPTDecimalFromInt(10);
yAxis.minorTicksPerInterval = 0;
yAxis.orthogonalCoordinateDecimal = CPTDecimalFromInteger(0);
//Setup Heart Rate YAxis
CPTXYAxis *rightY = [(CPTXYAxis *)[CPTXYAxis alloc] initWithFrame:CGRectZero];
rightY.coordinate = CPTCoordinateY;
rightY.plotSpace = plotSpace2;
rightY.orthogonalCoordinateDecimal = CPTDecimalFromInteger(0);
CPTMutableLineStyle *majorGridLineStyle = [CPTMutableLineStyle lineStyle];
majorGridLineStyle.lineWidth = 0.20;
majorGridLineStyle.lineColor = [CPTColor lightGrayColor];
yAxis.majorGridLineStyle = majorGridLineStyle;
yAxis.minorGridLineStyle = nil;
yAxis.majorTickLineStyle = nil;
yAxis.minorTickLineStyle = nil;
yAxis.axisLineStyle = nil;
yAxis.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
yAxis.preferredNumberOfMajorTicks = 8;
NSNumberFormatter *labelFormatter = [[NSNumberFormatter alloc] init];
labelFormatter.numberStyle = NSNumberFormatterDecimalStyle;
yAxis.labelFormatter = labelFormatter;
yAxis.labelOffset = 0;
// yAxis.titleOffset = 54;
yAxis.title = @"mmHg";
rightY.majorGridLineStyle = majorGridLineStyle;
rightY.minorGridLineStyle = nil;
rightY.majorTickLineStyle = nil;
rightY.minorTickLineStyle = nil;
rightY.axisLineStyle = nil;
rightY.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
rightY.preferredNumberOfMajorTicks = 8;
NSNumberFormatter *labelFormatter2 = [[NSNumberFormatter alloc] init];
labelFormatter.numberStyle = NSNumberFormatterDecimalStyle;
rightY.labelFormatter = labelFormatter2;
rightY.labelOffset = 0;
rightY.title = @"bpm";
NSMutableArray* exclusionRanges = [NSMutableArray array];
// // do not label 0
// [exclusionRanges addObject:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(1) length:CPTDecimalFromFloat(-99999)]];
//
// // do not label the daily goal, or nearby, because it gets a custom annotation
// if (dailyGoal > 0) {
// [exclusionRanges addObject:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(dailyGoal - maxY*0.05) length:CPTDecimalFromFloat(maxY*.1)]];
// }
// do not label max number, because sometimes the label is cut off at the top, which looks bad
[exclusionRanges addObject:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(maxY+1) length:CPTDecimalFromFloat(99999)]];
yAxis.labelExclusionRanges = exclusionRanges;
// yAxis.title = @"mmHg";
}
// add an annotation to label the daily goal.
// It is positioned at the left of the graph, just to the right of the axis, above 12AM.
// At this position we are mostly safe from interference from the daily steps graph.
// Also we do not put it over the Y labels, since those are automatically generated.
- (void)setupGoalAnnotation {
[self.graph.plotAreaFrame.plotArea removeAllAnnotations];
NSNumber* dailyGoal = 0;
if ([dailyGoal intValue] > 0) {
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)self.graph.axisSet;
CPTXYAxis *yAxis = axisSet.yAxis;
// custom labels for threshholds
CPTMutableTextStyle* textStyleThreshhold = [yAxis.labelTextStyle mutableCopy];
textStyleThreshhold.fontName = kLabelFontName;
textStyleThreshhold.textAlignment = CPTTextAlignmentCenter;
textStyleThreshhold.fontSize = textStyleThreshhold.fontSize - 4.0;
UIParam* uiParam = [UIParam sharedUIParam];
CPTBorderedLayer* layerAnnotation = [GraphUtil borderedTextLayer: [dailyGoal description] style:textStyleThreshhold margin:CGSizeMake(4, 4)];
layerAnnotation.fill = [CPTFill fillWithColor:[CPTColor whiteColor]];
layerAnnotation.borderColor = uiParam.colorGoalLine.CGColor;
layerAnnotation.borderWidth = 2.0;
CGFloat x = 0.0;
switch (self.data.dayInterval) {
case YMDayInterval1Day:
x = 22.9;
break;
case YMDayInterval7Day:
x = 6.60;
break;
case YMDayInterval14Day:
x = 13.2;
break;
default:
break;
}
NSArray* anchorPoint = @[@(x), dailyGoal ];
CPTPlotSpaceAnnotation* annotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:self.graph.defaultPlotSpace anchorPlotPoint:anchorPoint];
annotation.contentLayer = layerAnnotation;
[self.graph.plotAreaFrame.plotArea addAnnotation:annotation];
}
}
#pragma mark - CPTPlotDataSource delegate
- (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
if (plot == self.plotGoal) {
return 2;
}
else {
if (self.data.dayInterval == YMDayInterval7Day)
return 7;
return 14;
}
}
- (NSNumber *)numberForPlot:(CPTPlot *)plot field: (NSUInteger)fieldEnum recordIndex:(NSUInteger)idx {
if (plot == self.plotGoal) {
if (fieldEnum == CPTScatterPlotFieldX) {
if (idx == 0)
return @(-1);
else
return @(9999);
}
else if (fieldEnum == CPTScatterPlotFieldY) {
return 0;//self.data.stepsSummary.dailyStepsGoal.steps;
}
}
else {
if (fieldEnum == CPTBarPlotFieldBarLocation) {
return @(idx);
}
else if (fieldEnum == CPTBarPlotFieldBarTip) {
NSDate* date = [self.data.startDate dateByAddingTimeInterval: (SECONDS_PER_DAY * idx)];
for (YMBPSeries* bpEntry in self.data.bpSeries) {
if ([bpEntry.date isUTCSameDay:date]) {
if (bpEntry.systolic > 0)
return @(bpEntry.systolic);
else
return nil;
}
}
}
}
return nil;
}
@end
您走在正确的轨道上。使用一个图 space 作为带血压的条形图,使用第二个图 space 作为心率的散点图。将散点图的 plotSymbol
设置为所需的椭圆形状并填充并设置 dataLineStyle
为 nil
。这将在每个数据点绘制一个绘图符号,它们之间没有连接线。