Core plot: annotation with text and image - objective-c

I would like to add an annotation to my graph composed by a text and an image like this:
I can already display the text ("13" in the picture) but I'm not able to add the image below the text.
I've tried with CPTLayer, CPTBorderedLayer,..., but not of them work as expected.
Here is the code I'm using to display the text:
NSNumber *valueToDisplay = [NSNumber numberWithInt:13];
NSString *valueToDisplayString = [formatter stringFromNumber:valueToDisplay];
CPTTextLayer *textLayer = [[CPTTextLayer alloc] initWithText:valueToDisplayString style:style];
self.priceAnnotation.contentLayer = textLayer;
self.priceAnnotation.anchorPlotPoint = [NSArray arrayWithObjects:[NSNumber numberWithFloat:7.0], [NSNumber numberWithFloat:14.0], nil];
[self.graph.plotAreaFrame.plotArea addAnnotation:self.priceAnnotation];
How can I add the image below the text value ?
This is one of the pieces of code I've tried:
CPTBorderedLayer *immagine = [[CPTBorderedLayer alloc] initWithFrame:CGRectMake(0, 0, 77, 36)];
CPTFill *fillImage = [CPTFill fillWithImage:[CPTImage imageWithCGImage:[[UIImage imageNamed:#"sfondoStima.png"] CGImage]]];
immagine.fill = fillImage;
self.imageAnnotation.contentLayer = immagine;
self.imageAnnotation.anchorPlotPoint = [NSArray arrayWithObjects:[NSNumber numberWithFloat:7.0], [NSNumber numberWithFloat:5.0], nil];
[self.graph.plotAreaFrame.plotArea addAnnotation:self.imageAnnotation];
But this is the result: the bitmap (77x36) is for some reason much bigger than what it should be:
Please give me some help ... I've already tried different tutorial/example I've found but none of them seems to work.
Thanks,
Corrado

CPTTextLayer is a subclass of CPTBorderedLayer. For a simple background like this, I wouldn't bother with an image at all. I'd try something like this (untested):
CPTMutableLineStyle lineStyle = [CPTMutableLineStyle lineStyle];
lineStyle.lineWidth = 2.0;
lineStyle.lineColor = [CPTColor whiteColor];
CPTTextLayer *textLayer = [[CPTTextLayer alloc] initWithText:valueToDisplayString style:style];
textLayer.fill = [CPTFill fillWithColor:[CPTColor blueColor]];
textLayer.cornerRadius = 10.0;
textLayer.borderLineStyle = lineStyle;
Set the padding on the textLayer to control the space between the border line and the text.
If you have more complex needs that requires an image, be sure to set the image scale correctly. [CPTImage imageNamed:] does this for you.

Related

CorePlot LineGraph - hover/ click on graph to see values - macOS

I am using CorePlot to plot a simple Line Graph in my macOS app.
CPTXYGraph *newGraph = [[CPTXYGraph alloc] initWithFrame:CGRectZero];
CPTTheme *theme = [CPTTheme themeNamed:kCPTDarkGradientTheme];
[newGraph applyTheme:theme];
self.graph = newGraph;
self.hostView.hostedGraph = newGraph;
newGraph.plotAreaFrame.paddingTop = 10.0;
newGraph.plotAreaFrame.paddingBottom = 30.0;
newGraph.plotAreaFrame.paddingLeft = 40.0;
newGraph.plotAreaFrame.paddingRight = 10.0;
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)newGraph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:#(1.0) length:[NSNumber numberWithUnsignedInteger:[dataArray count]-1]];
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:#0.0 length:#102.0];
plotSpace.allowsUserInteraction = YES;
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)newGraph.axisSet;
CPTXYAxis *x = axisSet.xAxis;
//x.majorIntervalLength = #1;
x.majorIntervalLength = [NSNumber numberWithInt:numberOfIntervalsX];
x.orthogonalPosition = #(0);
x.minorTicksPerInterval = 0;
x.labelOffset = 0;
CPTXYAxis *y = axisSet.yAxis;
y.majorIntervalLength = #5;
y.minorTicksPerInterval = 0;
y.orthogonalPosition = #(1.0);
y.labelOffset = 0.0;
CPTScatterPlot *dataSourceLinePlot = [[CPTScatterPlot alloc] init];
CPTMutableLineStyle *lineStyle = [dataSourceLinePlot.dataLineStyle mutableCopy];
lineStyle.lineWidth = 2.;
lineStyle.lineColor = [CPTColor greenColor];
dataSourceLinePlot.dataLineStyle = lineStyle;
dataSourceLinePlot.dataSource = self;
[newGraph addPlot:dataSourceLinePlot];
I was expecting that the hover/ click to see values would be a default behavior but looks it is not. I have tried searching the forums but no luck. I am assuming it would be really straight forward. Not sure if I am missing something.
As far as i know, you're impression is correct, there is no built in data value overlay. However, you can make it yourself. CorePlot has the function indexOfVisiblePointClosestToPlotAreaPoint: that should give you the references needed to add a label w/ point value to your chart.
(NSUInteger) indexOfVisiblePointClosestToPlotAreaPoint:
Returns the index of the closest point, or NSNotFound if there is no visible point.
Then you can subclass your graph hostingview, implement a mouse movement event to capture mouse coordinates, and from there to do whatever logic you want to chose how to display the points.
I wouldn't say it's particularly easy to implement, but at least its straight foward. I hope it helps!
References:
http://core-plot.github.io/MacOS/interface_c_p_t_scatter_plot.html#a57eacc8261a4d4a1399f1196be786cff
https://stackoverflow.com/a/21819342/357288

Labeling Bar Chart in CorePlot

I have a horizontal bar chart using Coreplot in which I have labels within the bar itself with white text
like this
However, when the bar is to short to contain the label I would like the text Black and not offset like this.
Is there anything available to check if the label is clipped or compare the wide of the label to the bar length? So far I have this
-(CPTTextLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index
{
NSNumberFormatter *numberFormatter = [NSNumberFormatter percentFormatter];
NSDictionary *bar = [self.dataSource objectAtIndex:index];
NSDecimalNumber *xValue = [bar valueForKey:#"Value"];
NSString *stringLabel = [numberFormatter stringFromNumber:xValue];
CPTMutableTextStyle *whiteStyle = [[CPTMutableTextStyle alloc] init];
whiteStyle.color = [CPTColor whiteColor];
whiteStyle.fontSize = CPTFloat(12.0);
whiteStyle.fontName = #"HelveticaNeue-Medium";
CPTMutableTextStyle *darkStyle = [[CPTMutableTextStyle alloc] init];
darkStyle.color = [CPTColor blackColor];
darkStyle.fontSize = CPTFloat(12.0);
darkStyle.fontName = #"HelveticaNeue-Medium";
CPTTextLayer *textLayer = [[CPTTextLayer alloc] initWithText:stringLabel];
if (**isLabelClipped**) {
plot.labelOffset = 2.0f;
textLayer.textStyle = darkStyle;
} else {
plot.labelOffset = -2.0f;
textLayer.textStyle = whiteStyle;
}
return textLayer;
}
EDIT
As Eric suggested I attempted to to get the screen size from the plotspace, my thinking was I would use the difference between the xValue and the barsBase, so I added the following
NSDecimal plotPoint[2];
plotPoint[CPTCoordinateX] = xValue.decimalValue;
NSNumber *barBase = [self numberForPlot:plot
field:CPTBarPlotFieldBarBase
recordIndex:index];
plotPoint[CPTCoordinateY] = barBase.decimalValue;
CGPoint dataPoint = [plot.plotSpace plotAreaViewPointForPlotPoint:plotPoint numberOfCoordinates:2];
NSLog(#"Data Point:%#", NSStringFromPoint(dataPoint));
But this gives values like
{531.46951532736807, 57.166666666666664}
The barBase value (57..) looks to be the far left of the plot area and not the 0 axis as expected.
Knowing the bar length (from the xValue), you can use the plot space to determine its size on screen. After creating the textLayer, check its bounds size. Compare the two and decide whether to position the label inside or outside the bar.

Why Does Kerning fail for NSAttributedString in IOS7

My app has a UILabel formatted as an NSAttributedString with
the attribute: 'NSKernAttributeName #1.9,'
When the below code is compiled on iPad running IOS6, the kern works as expected.
When compiled on iPad running IOS7, no kerning occurs.
I have filed Bug at Apple Developer site. #15108371 - No Response yet
NSString *formattedNumber;
NSNumber *scoreNum = [[NSNumber alloc] initWithLongLong:thisScore];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterPadBeforeSuffix;
formatter.formatWidth = 10;
formatter.paddingCharacter = #"0";
formatter.numberStyle = NSNumberFormatterDecimalStyle;
formatter.usesGroupingSeparator = NO;
formattedNumber = [formatter stringFromNumber:scoreNum];
//Creat atributed string of formated number.
NSShadow *textShadow = [[NSShadow alloc] init];
textShadow.shadowColor = [UIColor colorWithRed:0.5 green:0.7 blue:1 alpha:1.0];
textShadow.shadowBlurRadius = 5.0;
textShadow.shadowOffset = CGSizeMake(0,0);
NSAttributedString *pHighScoreStyle = [[NSAttributedString alloc] initWithString:formattedNumber attributes: #{
NSFontAttributeName: [UIFont fontWithName:#"courier" size:16],
NSForegroundColorAttributeName: [UIColor colorWithRed:0.6 green:0.8 blue:1.0 alpha:0.8],
NSKernAttributeName: #1.9,
NSShadowAttributeName: textShadow } ];
//Change the disply value.
runningScore.attributedText = pHighScoreStyle;
OK. I had the same problem (see comment above). It depends on the font (I used Courier as well). For some strange reason Courier does not support kerning (in iOS7!). Use CourierNewPSMT and everything works as expected .... at least for me.
BTW: Here is a nice list of fonts on the iphone:
http://iosfonts.com/

Cocoa NSTextField line spacing

I'm struggling with a very simple problem, I've several NSTextField ( I can't use NSTextView right now) and I need to change the line spacing of the displayed text.
What can I do to reduce row height or line spacing? Shrinking the font size isn't an option.
Any help would be really appreciated!
Have a great weekend,
!)
For reference you want to read this description of paragraph styles: Cocoa Paragraph Styles and note that everything in there is additional space added between lines, between paragraphs, before paragraphs, etc. You can set the values in your NSMutableParagraphStyle to zero but no lower.
To further shrink the spacing between lines, use setMaximumLineHeight, thanks to "6 1" for the code (I've add the setMaximumLineHeight):
NSString *title = #"title here";
NSFont *bold14 = [NSFont boldSystemFontOfSize:14.0];
NSColor *textColor = [NSColor redColor];
NSMutableParagraphStyle *textParagraph = [[NSMutableParagraphStyle alloc] init];
[textParagraph setLineSpacing:10.0]; // this sets the space BETWEEN lines to 10points
[textParagraph setMaximumLineHeight:12.0]; this sets the MAXIMUM height of the lines to 12points
NSDictionary *attrDic = [NSDictionary dictionaryWithObjectsAndKeys:bold14, NSFontAttributeName, textColor, NSForegroundColorAttributeName, textParagraph, NSParagraphStyleAttributeName, nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:title attributes:attrDic];
[self.titleField setAllowsEditingTextAttributes:YES];
[self.titleField setAttributedStringValue:attrString];
Swift version of Jason Harrison's excellent Obj-c answer:
let title:String = "title here"
let bold14:NSFont = NSFont.boldSystemFontOfSize(14.0)
let textColor:NSColor = NSColor.redColor()
let textParagraph:NSMutableParagraphStyle = NSMutableParagraphStyle()
textParagraph.lineSpacing = 10.0 /*this sets the space BETWEEN lines to 10points*/
textParagraph.maximumLineHeight = 12.0/*this sets the MAXIMUM height of the lines to 12points*/
let attribs = [NSFontAttributeName:bold14,NSForegroundColorAttributeName:textColor,NSParagraphStyleAttributeName:textParagraph]
let attrString:NSAttributedString = NSAttributedString.init(string: title, attributes: attribs)
textField.attributedStringValue = attrString
you can use NSAttributedString to show the text.
NSFont *bold14 = [NSFont boldSystemFontOfSize:14.0];
NSColor *textColor = [NSColor redColor];
NSMutableParagraphStyle *textParagraph = [[NSMutableParagraphStyle alloc] init];
[textParagraph setLineSpacing:10.0];
NSDictionary *attrDic = [NSDictionary dictionaryWithObjectsAndKeys:bold14, NSFontAttributeName, textColor, NSForegroundColorAttributeName, textParagraph, NSParagraphStyleAttributeName, nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:title attributes:attrDic];
[self.titleField setAllowsEditingTextAttributes:YES];
[self.titleField setAttributedStringValue:attrString];
It's ok to display text not for inputing text. And I only know how to set line spacing.

Cocoa get string width in pixels from font

I am trying to find the width in pixels of a string from the font and font size. I am currently using this code, but it is not working 100% of the time. Is there another way to do it?
NSSize textSize = [aTextLayer.string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:#"Bank Gothic Medium", NSFontNameAttribute, [NSNumber numberWithFloat:aTextLayer.fontSize], NSFontSizeAttribute, nil]];
NSAttributedString is granted a -size method by the Application Kit Additions.
NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
#"Bank Gothic Medium", NSFontNameAttribute,
[NSNumber numberWithFloat:aTextLayer.fontSize], NSFontSizeAttribute,
nil];
NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:aTextLayer.string attributes:attributes];
NSSize size = attributedString.size;
Here is what i use to get the size of a string...
NSSize size = [#"Some text" sizeWithAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:#"Helvetica Neue Bold" size:24.0f] forKey:NSFontAttributeName]];
NOTE: If you are adding the string to a textfield, i have found that you need to add about 10 to size.width for it to fit.
Try using the actual NSFont (or UIFont) object instead of just the name of the font.
Here is yet another example:
NSString *test = [[NSString alloc] initWithFormat:#"%u:%u:%u.000", hours, minutes, seconds];
NSSize boundingSize = {100,300}; //I suppose this is the constraints?
NSRect boundingRect = [test boundingRectWithSize:boundingSize options:NULL attributes:stringAttributes];
point.x -= boundingRect.size.width; //This point points at the end of screen
[test drawAtPoint:point withAttributes:stringAttributes];
Here is for the stringAttributes, that may help noobs like me:
NSMutableDictionary *stringAttributes;
stringAttributes = [NSMutableDictionary dictionary];
[stringAttributes setObject:[NSFont fontWithName:#"Monaco" size:16] forKey:NSFontAttributeName];
[stringAttributes setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
[stringAttributes setObject:[NSColor blackColor] forKey:NSBackgroundColorAttributeName];