使用垂直条形图示例作为开始,我创建了一个图表,其y值为0..1,x值为1..n但为我的数据源的CPTBarPlotFieldBarLocation单元格提供的x标签索引不具备出现;我没有使用y轴标签。
我怀疑我剪掉了它们。有没有办法像旧IB一样绘制各种图形区域的边界矩形?
x轴绘图标签是示例中的非数字字符串。我打破了什么?这是我的设置
//
// InfoBarView.m
#import "InfoBarView.h"
// Item index to color mapping for legend symbols not item related
static NSUInteger fldMapping[] = { 5, 6, 7, 0 };
static const BOOL kUseHorizontalBars = NO;
static NSArray <__kindof NSString *> const* kTargets = nil;
static NSArray <__kindof NSString *> const* kGetters = nil;
@interface BarChart()
@property (nonatomic, readwrite, strong, nullable) CPTPlotSpaceAnnotation * symbolTextAnnotation;
@end
@implementation BarChart
@synthesize symbolTextAnnotation;
@synthesize plotData;
+ (void)initialize
{
kTargets = [@[ @"grpA" , @"grpB", @"grpC", @"grpD" ] retain];
kGetters = [@[ @"fldA", @"fldB", @"fldC", @"fldD" ] retain];
}
- (void)awakeFromNib
{
// Get our orig scaling
[super awakeFromNib];
}
- (void)killGraph
{
if ( self.graphs.count )
{
CPTGraph * graph = self.defaultGraph;
CPTPlotSpaceAnnotation *annotation = self.symbolTextAnnotation;
if ( annotation )
{
[graph.plotAreaFrame.plotArea removeAnnotation:annotation];
self.symbolTextAnnotation = nil;
}
}
[super killGraph];
}
- (void)generateData
{
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
NSUInteger rndx = controller.barDataRanking;
NSArray * rankings = @[ @"Rank", @"Runs" ];
GroupItem * group = controller.group;
CPTGraph * graph = [self defaultGraph];
CPTPlotSpaceAnnotation * annotation = self.symbolTextAnnotation;
if ( annotation )
{
if (graph)
{
[graph.plotAreaFrame.plotArea removeAnnotation:annotation];
self.symbolTextAnnotation = nil;
}
}
// Update X title with new controller index
if (graph)
{
graph.title = [NSString stringWithFormat:@"Past Performances by %@", rankings[rndx]];
}
// Figure out our rank selector
NSString * rankSelectorName = [NSString stringWithFormat:@"%@%@Compare:",
kTargets[controller.barDataIndex],
rankings[rndx]];
SEL dataRanking = NSSelectorFromString(rankSelectorName);
self.plotData = (id)[group.items sortedArrayUsingSelector:dataRanking];
}
- (void)renderInGraphHostingView:(nonnull CPTGraphHostingView *)hostingView
withTheme:(nullable CPTTheme *)theme
animated:(BOOL)animated
{
#if TARGET_OS_SIMULATOR || TARGET_OS_IPHONE
CGRect bounds = hostingView.bounds;
#else
CGRect bounds = NSRectToCGRect(hostingView.bounds);
#endif
InfoController * controller = ((InfoBarView *)hostingView.superview).controller;
CPTGraph * graph = [[[CPTXYGraph alloc] initWithFrame:bounds] autorelease];
NSString * titles = @"fldA fldB fldC fldD";
GroupItem * group = controller.group;
CPTBarPlot * barPlot;
// Initially we default by rank
self.title = @"Past Performances by Rank";
[self addGraph:graph toHostingView:hostingView];
graph.plotAreaFrame.masksToBorder = NO;
if ( kUseHorizontalBars )
{
graph.plotAreaFrame.paddingBottom += self.titleSize;
}
else
{
graph.plotAreaFrame.paddingLeft += self.titleSize;
}
// Add plot space for bar charts
CPTXYPlotSpace * barPlotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;
[barPlotSpace setScaleType:CPTScaleTypeCategory forCoordinate:CPTCoordinateX];
if ( kUseHorizontalBars )
{
barPlotSpace.xRange = [CPTPlotRange plotRangeWithLocation:@(-10.0) length:@120.0];
barPlotSpace.yRange = [CPTPlotRange plotRangeWithLocation:@(-1.0) length:@11.0];
}
else
{
barPlotSpace.xRange = [CPTPlotRange plotRangeWithLocation:@(-0.5) length:@(group.itemCount)];
barPlotSpace.yRange = [CPTPlotRange plotRangeWithLocation:@(-0.05) length:@1.0];
}
barPlotSpace.allowsUserInteraction = YES;
[graph addPlotSpace:barPlotSpace];
barPlotSpace.delegate = self;
// Create grid line styles
CPTMutableLineStyle * majorGridLineStyle = [CPTMutableLineStyle lineStyle];
majorGridLineStyle.lineWidth = 1.0;
majorGridLineStyle.lineColor = [[CPTColor grayColor] colorWithAlphaComponent:0.75];
CPTMutableLineStyle * minorGridLineStyle = [CPTMutableLineStyle lineStyle];
minorGridLineStyle.lineWidth = 1.0;
minorGridLineStyle.lineColor = [[CPTColor whiteColor] colorWithAlphaComponent:0.25];
// Create axes
CPTXYAxisSet * axisSet = (CPTXYAxisSet *)graph.axisSet;
CPTXYAxis * x = axisSet.xAxis;
{
x.majorIntervalLength = (kUseHorizontalBars ? @10.0 : @1.0);
x.minorTicksPerInterval = (kUseHorizontalBars ? 9 : 0);
x.orthogonalPosition = (kUseHorizontalBars ? @(-0.5) : @0.0);
x.majorGridLineStyle = majorGridLineStyle;
x.minorGridLineStyle = minorGridLineStyle;
x.axisLineStyle = nil;
x.majorTickLineStyle = nil;
x.minorTickLineStyle = nil;
x.labelOffset = self.titleSize * CPTFloat(2.5);
if ( kUseHorizontalBars )
{
x.visibleRange = [CPTPlotRange plotRangeWithLocation:@0.0 length:@10.0];
x.gridLinesRange = [CPTPlotRange plotRangeWithLocation:@(-0.5) length:@(group.itemCount)];
}
else
{
x.visibleRange = [CPTPlotRange plotRangeWithLocation:@(-0.5) length:@(group.itemCount)];
x.gridLinesRange = [CPTPlotRange plotRangeWithLocation:@0.0 length:@(1.0)];
}
x.plotSpace = barPlotSpace;
}
CPTXYAxis * y = axisSet.yAxis;
{
y.majorIntervalLength = (kUseHorizontalBars ? @1.0 : @10.0);
y.minorTicksPerInterval = (kUseHorizontalBars ? 0 : 9);
y.orthogonalPosition = (kUseHorizontalBars ? @0.0 : @(-0.5) );
y.preferredNumberOfMajorTicks = 8;
y.majorGridLineStyle = majorGridLineStyle;
y.minorGridLineStyle = minorGridLineStyle;
y.axisLineStyle = nil;
y.majorTickLineStyle = nil;
y.minorTickLineStyle = nil;
y.labelOffset = self.titleSize * CPTFloat(0.175);
y.titleLocation = (kUseHorizontalBars ? @55.0 : @(group.itemCount-1));
if ( kUseHorizontalBars )
{
y.visibleRange = [CPTPlotRange plotRangeWithLocation:@(-0.5) length:@10.0];
y.gridLinesRange = [CPTPlotRange plotRangeWithLocation:@0.0 length:@100.0];
}
else
{
y.visibleRange = [CPTPlotRange plotRangeWithLocation:@(-0.5) length:@(group.itemCount)];
y.gridLinesRange = [CPTPlotRange plotRangeWithLocation:@(-0.5) length:@(group.itemCount)];
}
y.title = titles;
y.titleOffset = self.titleSize * CPTFloat(0.25);
[barPlotSpace addCategory:title[seg] forCoordinate:CPTCoordinateY];
y.plotSpace = barPlotSpace;
}
// Set axes
graph.axisSet.axes = @[x, y];
// Create a shared bar line style
CPTMutableLineStyle * barLineStyle = [[[CPTMutableLineStyle alloc] init] autorelease];
barLineStyle.lineWidth = 1.0;
barLineStyle.lineColor = [CPTColor whiteColor];
// Marshall Y axis into individual plot identifiers
NSArray * title = [titles componentsSeparatedByString:@" "];
CPTMutableTextStyle * blackTextStyle = [CPTMutableTextStyle textStyle];
blackTextStyle.color = [CPTColor blackColor];
// Create bar plot(s)
for (int seg=0; seg<title.count; seg++)
{
barPlot = [[[CPTBarPlot alloc] init] autorelease];
barPlot.lineStyle = barLineStyle;
barPlot.fill = [CPTFill fillWithColor:[CPTColor colorAtIndex:seg]];
barPlot.barBasesVary = YES;
barPlot.barCornerRadius = 4.0;
barPlot.barWidth = @0.5;
barPlot.barsAreHorizontal = kUseHorizontalBars;
barPlot.dataSource = self;
barPlot.delegate = self;
barPlot.identifier = [NSString stringWithFormat:@"%@,%d",title[seg],seg],
barPlot.labelTextStyle = blackTextStyle;
barPlot.labelOffset = 0.0;
[graph addPlot:barPlot toPlotSpace:barPlotSpace];
NSLog(@"render:%@", barPlot.identifier);
}
// Add X category per slot
for (Item * item in plotData)
{
[barPlotSpace addCategory:item.numb forCoordinate:CPTCoordinateX];
}
}
#pragma mark -
#pragma mark Plot Delegate
- (void)Plot:(nonnull CPTPlot *)plot dataLabelWasSelectedAtRecordIndex:(NSUInteger)index
{
// NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:@","] lastObject];
NSLog(@"Data label for '%@' was selected at record %d.", plot.identifier, (int)index);
}
- (CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index;
{
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
CPTMutableTextStyle * textStyle = [CPTMutableTextStyle textStyle];
textStyle.color = [CPTColor colorAtIndex:item.foreRGB];
textStyle.textAlignment = CPTTextAlignmentCenter;
textStyle.fontSize = 24;
plot.labelOffset = 0;
CPTMutableShadow * shadow = [CPTMutableShadow shadow];
shadow.shadowColor = [CPTColor blackColor];//colorAtIndex:item.backRGB];
shadow.shadowOffset = CGSizeMake(1,-1);
shadow.shadowBlurRadius = 2.0f;
plot.labelShadow = shadow;
CPTTextLayer * textLayer = [[CPTTextLayer alloc] initWithText:item.numb.squish style:textStyle];
return [textLayer autorelease];
}
- (void)barPlot:(CPTBarPlot *)plot barWasSelectedAtRecordIndex:(NSUInteger)index
{
CGFloat floor = [[self valueForPlot:plot field:CPTBarPlotFieldBarBase recordIndex:index] doubleValue];
CGFloat value = [[self valueForPlot:plot field:CPTBarPlotFieldBarTip recordIndex:index] doubleValue];
CGFloat runs = 0;
NSString * barID = (NSString *)[[(id)plot.identifier
componentsSeparatedByString:@","] lastObject];
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int fndx = [barID intValue];
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
SEL dataItem = NSSelectorFromString(kTargets[controller.barDataIndex]);
DataItem * data = [item performSelector:dataItem];
runs = [[data fldD] doubleValue];
NSLog(@"BarSelected %@ %@ record:%lu. base:%.2f value:%.2f",
plot.identifier, data.name, index, floor, value);
CPTGraph * graph = [self defaultGraph];
CPTPlotSpaceAnnotation * annotation = self.symbolTextAnnotation;
if ( annotation )
{
[graph.plotAreaFrame.plotArea removeAnnotation:annotation];
self.symbolTextAnnotation = nil;
}
// Setup a style for the annotation
CPTMutableTextStyle * hitAnnotationTextStyle = [CPTMutableTextStyle textStyle];
hitAnnotationTextStyle.color = [CPTColor colorAtIndex:fldMapping[fndx]];
hitAnnotationTextStyle.fontSize = 16.0;
hitAnnotationTextStyle.fontName = @"Helvetica-Bold";
// Determine point of symbol in plot coordinates
NSNumber * x = @(index);
NSNumber * y = @(0.03 + (floor / runs));
CPTNumberArray * anchorPoint = (kUseHorizontalBars ? @[y, x] : @[x, y]);
// Add annotation
// First make a string for the y value
NSString * yString = [[NSNumberFormatter withFormat:@"#,###"] stringFromNumber:@(value)];
// Now add the annotation to the plot area
CPTPlotSpace * space = plot.plotSpace;
if ( space )
{
CPTTextLayer * textLayer = [[CPTTextLayer alloc] initWithText:yString style:hitAnnotationTextStyle];
annotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:space anchorPlotPoint:anchorPoint];
annotation.contentLayer = [textLayer autorelease];
annotation.displacement = CGPointMake(0.0, 0.0);
self.symbolTextAnnotation = annotation;
[graph.plotAreaFrame.plotArea addAnnotation:[annotation autorelease]];
}
}
#pragma mark Plot DataSource
- (NSUInteger)numberOfRecordsForPlot:(nonnull CPTPlot *)plot
{
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
return group.itemCount;
}
- (nullable id)numberForPlot:(nonnull CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:@","] lastObject];
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int fndx = [barID intValue];
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
SEL dataItem = NSSelectorFromString(kTargets[controller.barDataIndex]);
NSString * getName = kGetters[fndx];
SEL getter = NSSelectorFromString(getName);
SEL fldD = @selector(fldD);
CGFloat runs, base=0, value=0;
DataItem * data;
id num=nil;
// First get which data data item we're after
data = [item performSelector:dataItem];
runs = [[data performSelector:fldD] floatValue];
// Now fetch its getter
switch ( fieldEnum )
{
case CPTBarPlotFieldBarLocation:
num = item.numb;
break;
case CPTBarPlotFieldBarTip:
// Build base for this tip
for (int seg=0; seg < fndx; seg++)
{
value += [[data performSelector:NSSelectorFromString(kGetters[seg])] floatValue];
}
// Add this 'tip' value unless it's the total
if (fndx == 3)
num = @(1.0f);
else
{
value += [[data performSelector:getter] floatValue];
num = @(value/runs);
}
break;
case CPTBarPlotFieldBarBase:
// Calc base by sum prior fields
for (int seg=0; seg < fndx; seg++)
{
base += [[data performSelector:NSSelectorFromString(kGetters[seg])] floatValue];
}
num = @(base/runs);
}
return num;
}
- (nullable id)valueForPlot:(nonnull CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:@","] lastObject];
InfoBarView * barView = (id)[[self defaultLayerHostingView] superview];
InfoController * controller = barView.controller;
GroupItem * group = controller.group;
int fndx = [barID intValue];
int sndx = index % group.itemCount;
Item * item = (Item *)[self.plotData objectAtIndex:sndx];
SEL dataItem = NSSelectorFromString(kTargets[controller.barDataIndex]);
NSString * getName = kGetters[fndx];
SEL getter = NSSelectorFromString(getName);
SEL fldD = @selector(fldD);
CGFloat runs, value=0;
DataItem * data;
id num=nil;
// First get which data block we're after
data = [item performSelector:dataItem];
runs = [[data performSelector:fldD] floatValue];
// Now fetch its getter
switch ( fieldEnum )
{
case CPTBarPlotFieldBarLocation:
num = item.numb.squish;
break;
case CPTBarPlotFieldBarTip:
value = [[data performSelector:getter] floatValue];
num = @(value);
break;
case CPTBarPlotFieldBarBase:
// Calc base by sum prior fields
for (int seg=0; seg < fndx; seg++)
{
value += [[data performSelector:NSSelectorFromString(kGetters[seg])] floatValue];
}
num = @(value);
}
return num;
}
- (nullable NSString *)legendTitleForBarPlot:(nonnull CPTBarPlot *)plot recordIndex:(NSUInteger)index
{
NSString * barID = (NSString *)[[(id)plot.identifier componentsSeparatedByString:@","] lastObject];
int fndx = [barID intValue];
return kGetters[fndx];
}
#pragma mark Plot Legend Delegate
- (void)legend:(CPTLegend *)legend legendEntryForPlot:(CPTPlot *)plot wasSelectedAtIndex:(NSUInteger)index
{
NSLog(@"legend:%@ index:%lu", plot.identifier, index);
}
#pragma mark Plot Space Delegate
#pragma mark Plot Space Delegate
- (CPTPlotRange *)plotSpace:(CPTPlotSpace *)space
willChangePlotRangeTo:(CPTPlotRange *)newRange
forCoordinate:(CPTCoordinate)coordinate
{
CGFloat newLocation = newRange.location.floatValue;
// CGFloat newLength = newRange.length.floatValue;
// Range change to +2 point on data boundary
if (CPTCoordinateX == coordinate)
{
if (newLocation < -0.5)
{
return [CPTPlotRange plotRangeWithLocation:@(-0.5) length:newRange.length];
}
else
if (newLocation > (0.3))
{
return [CPTPlotRange plotRangeWithLocation:@(0.3) length:newRange.length];
}
}
if (CPTCoordinateY == coordinate)
{
if (newLocation < (-0.1))
{
return [CPTPlotRange plotRangeWithLocation:@(-0.1) length:newRange.length];
}
else
if (newLocation > (0.1))
{
return [CPTPlotRange plotRangeWithLocation:@(0.1) length:newRange.length];
}
}
// CGRect chartBounds = self.defaultGraph.bounds;
// NSLog(@"old:%@", NSStringFromRect(chartBounds));
// NSLog(@"new:%@ %@", (coordinate == CPTCoordinateX ? @"X" : @"Y"),[newRange description]);
return newRange;
}
@end
@implementation InfoBarView
@synthesize barChart;
@synthesize x_title;
- (void)awakeFromNib
{
// Get our orig scaling
[super awakeFromNib];
self.barChart = [[[BarChart alloc] init] autorelease];
}
- (void)drawRect:(NSRect)clip
{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
NSImage * webImage = controller.group.track.webImage;
GroupItem * group = controller.group;
NSRect rect = [self frame];
// Save state so we can restore later
CGContextSaveGState(context);
// Draw track image as our view's background
if (webImage)
{
[webImage drawInRect:rect fromRect:[webImage alignmentRect]
operation:NSCompositeSourceOver fraction:0.25];
}
if (group && !barChart.graphs.count)
{
[barChart renderInView:self withTheme:nil animated:YES];
}
CGContextRestoreGState(context);
}
@end
如果有明显的线索 - 绘制了界限,我认为我们中的很多人都可以从中受益(我想念那些来自IB大约第3版的人)。
答案 0 :(得分:0)
语句{{1}}消除了自动轴标签。如果您提供标签格式化器和标签文本样式,则轴将根据标签策略自动创建刻度和标签。